module(..., package.seeall)


-- see http://homepages.cwi.nl/~steven/css/hsl.html

function _h2rgb(m1, m2, h)
   if h<0 then h = h+1 end
   if h>1 then h = h-1 end

   if h*6<1 then 
      return m1+(m2-m1)*h*6
   elseif h*2<1 then 
      return m2 
   elseif h*3<2 then 
      return m1+(m2-m1)*(2/3-h)*6
   else
      return m1
   end
end

function hsl_to_rgb(h, s, L)
   h = h/360
   local m1, m2
   if L<=0.5 then 
      m2 = L*(s+1)
   else 
      m2 = L+s-L*s
   end

   m1 = L*2-m2

   return _h2rgb(m1, m2, h+1/3), _h2rgb(m1, m2, h), _h2rgb(m1, m2, h-1/3)
end


Color = {

   new = function(self, H, S, L) 
      obj = {H = H, S = S, L = L}
      setmetatable(obj, self)		
      self.__index = self
      self.__tostring = self.to_rgb
      return obj
   end,

   to_rgb = function(self)
      local r, g, b = hsl_to_rgb(self.H, self.S, self.L)
      local rgb = {hsl_to_rgb(self.H, self.S, self.L)}
      buffer = "#"
      for i,v in ipairs(rgb) do
	 buffer = buffer..string.format("%02x",math.floor(v*255))
      end
      return buffer
   end,

   hue_offset = function(self, delta)
      return self:new((self.H + delta) % 360, self.S, self.L)
   end,

   complementary = function(self) 
      return self:hue_offset(180)
   end,

   neighbors = function(self, angle)
      angle = angle or 30
      return self:hue_offset(angle), self:hue_offset(360-angle)
   end,

   triadic = function(self) 
      return self:neighbors(120)
   end,

   split_complementary = function(self, angle)
      angle = angle or 30
      return self:neighbors(180-angle)
   end,

   desaturate_to = function(self, v)
      return self:new(self.H, v, self.L)
   end,

   desaturate_by = function(self, r)
      return self:new(self.H, self.S*r, self.L)
   end,	      

   lighten_to = function(self, v)
      return self:new(self.H, self.S, v)
   end,

   lighten_by = function(self, r)
      return self:new(self.H, self.S, r)
   end,

   variations = function(self, f, n)
      n = n or 5
      local results = {}
      for i=1,n do
	 table.insert(results, f(self, i, n))
      end
      return results
   end,

   tints = function(self, n)
      local f = function (color, i, n) 
		   return color:lighten_to(color.L + (1-color.L)/n*i)
		end
      return self:variations(f, n)
   end,

   shades = function(self, n)
      local f = function (color, i, n) 
		   return color:lighten_to(color.L - (color.L)/n*i)
		end
      return self:variations(f, n)
   end,

   tint = function(self, r)
      return self:lighten_to(self.L + (1-self.L)*r)
   end,

   shade = function(self,r)
      return self:lighten_to(self.L - self.L*r)
   end,

}






function myyield(c, f)
   return function()
	     for i, c2 in ipairs(f(c, 12)) do
		cosmo.yield{rgb = c2:to_rgb(),
		   name = string.format("%d-%d-%d",
					c2.H, c2.S, c2.L)
		}
	     end
	  end
end


function test() 

   require"cosmo"

   local template = [==[
      <table border="1">
        $docolors[=[
	   <tr>
	      <td bgcolor='$base'>$base</td>
	      $dotints[[<td bgcolor='$rgb'>&nbsp;&nbsp;&nbsp;&nbsp;</td>]]
	   </tr>
	   <tr> 
	      <td bgcolor='$base'>&nbsp;</td>        
	      $doshades[[<td bgcolor='$rgb'>&nbsp;&nbsp;&nbsp;&nbsp;</td>]]
	   </tr>
	]=]
     </table>
  ]==]

  local c = Color:new(20, .9, .50)
  local c2, c3 = c:neighbors(20) 
  --c2, c3 = c:triadic() --complementary()
  local colors = {c, c2, c3}

  print(cosmo.fill(template,
		   { docolors = function() 
				   for i, c2 in ipairs(colors) do
				      cosmo.yield {
					 base = c2:to_rgb(),
					 dotints = myyield(c2, c2.tints),
					 doshades = myyield(c2, c2.shades),
				      }
				   end
				end,
		   }
		)
     )

end

--test()

